import yaml
import av
import threading
import queue
import time
import logging
from PIL import Image, ImageChops
import traceback
import sys
import requests  # 需安装: pip install requests
from io import BytesIO

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - Media - %(levelname)s - %(message)s')
logger = logging.getLogger("MediaPlayer")

# 预加载无信号参考图
NO_SIGNAL_REF_PATH = "asset/no_signal_reference.png"
try:
    NO_SIGNAL_REF = Image.open(NO_SIGNAL_REF_PATH)
    logger.info(f"成功加载无信号参考图: {NO_SIGNAL_REF_PATH}")
except Exception as e:
    NO_SIGNAL_REF = None
    logger.warning(f"无信号参考图加载失败: {str(e)}，跳过信号检测逻辑")


class Media:
    def __init__(self, config_path=None, error_callback=None, status_callback=None):
        # 原有配置
        self.config = self._load_config(config_path) if config_path else {}
        self.player = self.config.get('player', {})
        self.url = self.player.get('url', '')

        # 帧转发配置（新增）
        self.forward = self.config.get('forward', {})
        self.forward_url = self.forward.get('url', 'http://localhost:8080/frame')  # 浏览器接收端点
        self.forward_interval = self.forward.get('interval', 0.1)  # 发送间隔（秒）
        self.forward_enabled = self.forward.get('enabled', True)  # 是否启用转发

        # 帧队列
        self.FRAME_QUEUE_SIZE = 3
        self.frame_queue = queue.Queue(maxsize=self.FRAME_QUEUE_SIZE)
        self.queue_lock = threading.Lock()

        # 占位图与无信号参考图
        self.placeholder_path = "asset/placeholder.jpg"
        try:
            self.placeholder_img = Image.open(self.placeholder_path)
            logger.info(f"成功加载placeholder图片: {self.placeholder_path}")
        except Exception as e:
            self.placeholder_img = Image.new('RGB', (640, 480), color='black')
            logger.warning(f"placeholder图片加载失败: {str(e)}，使用空白图替代")
        self.no_signal_reference = NO_SIGNAL_REF

        # 核心状态
        self.state_lock = threading.Lock()
        self.playing = False
        self.in_no_signal = False
        self.hold_placeholder = False
        self.hold_timer = None
        self.HOLD_DURATION = 15

        # 流资源
        self.thread = None
        self.forward_thread = None  # 帧转发线程（新增）
        self.container = None
        self.stream = None

        # 帧统计
        self.frame_count = 0
        self.last_frame_received = time.time()
        self.forward_count = 0  # 转发计数（新增）

        # 回调函数
        self.error_callback = error_callback
        self.status_callback = status_callback

        # 重连参数
        self.reconnecting = False
        self.reconnect_interval = 5
        self.reconnect_attempts = 0

        # 转发控制（新增）
        self.forward_running = False

    def _load_config(self, config_path):
        """加载配置文件（包含转发配置）"""
        try:
            with open(config_path, "r", encoding="utf-8") as f:
                return yaml.safe_load(f)
        except Exception as e:
            logger.error(f"加载配置文件失败: {str(e)}")
            if self.error_callback:
                self.error_callback(f"config_load_error: {str(e)}")
            return {}

    def start(self):
        """启动播放与帧转发"""
        with self.state_lock:
            if self.playing:
                logger.warning("已在运行中，无需重复启动")
                return True

            # 重置状态
            self.playing = True
            self.reconnecting = False
            self.reconnect_attempts = 0
            self.in_no_signal = False
            self.hold_placeholder = False
            self._clear_queue()

            # 启动帧转发线程（新增）
            if self.forward_enabled:
                self.forward_running = True
                self.forward_thread = threading.Thread(target=self._forward_frames_loop, daemon=True)
                self.forward_thread.start()
                logger.info("帧转发线程已启动")

        if self.status_callback:
            self.status_callback("starting")

        # 启动帧读取线程
        self.thread = threading.Thread(target=self._read_frames_loop, daemon=True)
        self.thread.start()
        logger.info("Media已启动，将持续运行直至收到停止指令")
        return True

    def stop(self):
        """停止运行（含帧转发）"""
        with self.state_lock:
            if not self.playing:
                logger.warning("未在运行中，无需停止")
                return True

            # 停止核心标志
            self.playing = False
            self.reconnecting = False
            self.forward_running = False  # 停止转发（新增）

        # 清理资源
        self._cleanup_resources()

        # 等待线程退出
        if self.thread and self.thread.is_alive():
            self.thread.join(timeout=5)
            if self.thread.is_alive():
                logger.warning("帧读取线程未能及时退出")

        # 等待转发线程退出（新增）
        if self.forward_thread and self.forward_thread.is_alive():
            self.forward_thread.join(timeout=5)
            if self.forward_thread.is_alive():
                logger.warning("帧转发线程未能及时退出")

        # 清空队列
        self._clear_queue()

        if self.status_callback:
            self.status_callback("stopped")
        logger.info("Media已停止运行")
        return True

    def get_latest_frame(self):
        """获取最新帧（原有逻辑不变）"""
        frame = None
        try:
            with self.queue_lock:
                while not self.frame_queue.empty():
                    frame = self.frame_queue.get_nowait()

            with self.state_lock:
                if self.in_no_signal:
                    if frame and not self._is_no_signal(frame):
                        self.in_no_signal = False
                        self.hold_placeholder = True
                        self.hold_timer = time.time() + self.HOLD_DURATION
                        logger.info("信号恢复，进入占位阶段")
                        return self.placeholder_img.copy()

                    if self.no_signal_reference and frame and self._is_no_signal(frame):
                        return self.no_signal_reference.copy()

                    if self.no_signal_reference and not frame:
                        return self.no_signal_reference.copy()

                if self.hold_placeholder:
                    if time.time() < self.hold_timer:
                        return self.placeholder_img.copy()
                    else:
                        self.hold_placeholder = False
                        logger.info("占位阶段结束，恢复正常帧")

                if self.no_signal_reference and frame and self._is_no_signal(frame):
                    self.in_no_signal = True
                    logger.info("检测到无信号帧，进入无信号状态")
                    return self.no_signal_reference.copy()

            return frame

        except queue.Empty:
            if self.no_signal_reference:
                return self.no_signal_reference.copy()
            return None
        except Exception as e:
            logger.error(f"获取帧失败: {str(e)}")
            if self.error_callback:
                self.error_callback(f"get_frame_error: {str(e)}")
            return None

    def _forward_frames_loop(self):
        """帧转发线程（新增）：持续将帧POST到浏览器"""
        logger.info(f"帧转发线程启动，目标URL: {self.forward_url}，间隔: {self.forward_interval}秒")
        while self.forward_running:
            try:
                # 检查是否需要发送
                frame = self.get_latest_frame()
                if frame:
                    # 转换为JPEG格式（浏览器可直接显示）
                    jpeg_data = self._image_to_jpeg(frame)
                    if jpeg_data:
                        # 发送POST请求
                        self._post_frame(jpeg_data)
                        self.forward_count += 1
                        if self.forward_count % 10 == 0:  # 每10帧打印一次统计
                            logger.info(f"已转发 {self.forward_count} 帧")

                # 控制发送频率
                time.sleep(self.forward_interval)

            except Exception as e:
                logger.error(f"帧转发异常: {str(e)}")
                if self.error_callback:
                    self.error_callback(f"forward_error: {str(e)}")
                time.sleep(1)  # 出错时延长等待

        logger.info("帧转发线程已退出")

    def _image_to_jpeg(self, image):
        """将PIL图像转换为JPEG字节数据"""
        try:
            buffer = BytesIO()
            # 压缩为JPEG（质量80，平衡大小和清晰度）
            image.save(buffer, format='JPEG', quality=80)
            return buffer.getvalue()
        except Exception as e:
            logger.error(f"图像转JPEG失败: {str(e)}")
            return None

    def _post_frame(self, jpeg_data):
        """发送JPEG数据到浏览器的HTTP端点"""
        try:
            # 设置请求头（浏览器可识别为JPEG）
            headers = {'Content-Type': 'image/jpeg'}
            # 发送POST请求（超时5秒）
            response = requests.post(
                self.forward_url,
                data=jpeg_data,
                headers=headers,
                timeout=5
            )
            # 检查响应状态
            if response.status_code != 200:
                logger.warning(f"帧转发失败，HTTP状态码: {response.status_code}")
            else:
                # 可选：处理浏览器返回的响应数据
                pass
        except requests.exceptions.RequestException as e:
            logger.error(f"POST请求失败: {str(e)}")

    # 以下为原有方法（保持不变）
    def is_running(self):
        with self.state_lock:
            return self.playing

    def get_status(self):
        with self.state_lock:
            if not self.playing:
                return "stopped"
            if self.reconnecting:
                return "reconnecting"
            if self.in_no_signal:
                return "no_signal"
            if self.hold_placeholder:
                return "placeholder"
            return "running"

    def _read_frames_loop(self):
        logger.info("帧读取线程启动，开始持续运行")
        while True:
            with self.state_lock:
                if not self.playing:
                    logger.info("收到停止指令，帧读取线程退出")
                    break

            try:
                self._open_stream()
                self._frame_read_loop()

            except Exception as e:
                logger.error(f"帧读取异常: {str(e)}\n{traceback.format_exc()}")
                if self.error_callback:
                    self.error_callback(f"read_frame_error: {str(e)}")

                self._cleanup_resources()

                with self.state_lock:
                    if self.playing and not self.reconnecting:
                        self._start_reconnecting()

        logger.info("帧读取线程已退出")

    def _open_stream(self):
        if self.status_callback:
            self.status_callback("connecting")

        try:
            self.container = av.open(
                self.url,
                mode='r',
                options={
                    'rtsp_transport': 'tcp',
                    'stimeout': '5000000',
                    'buffer_size': '262144',
                    'fflags': 'flush_packets',
                    'flags': 'low_delay',
                    'max_delay': '20000',
                    'analyzeduration': '50000',
                    'probesize': '16384'
                }
            )

            self.stream = next((s for s in self.container.streams if s.type == 'video'), None)
            if not self.stream:
                raise Exception("未找到视频流轨道")

            with self.state_lock:
                self.reconnecting = False
                self.reconnect_attempts = 0

            if self.status_callback:
                self.status_callback("connected")
                self.status_callback("running")

            logger.info(f"成功连接到视频流: {self.url}")

        except Exception as e:
            raise Exception(f"打开视频流失败: {str(e)}")

    def _frame_read_loop(self):
        try:
            for packet in self.container.demux(self.stream):
                with self.state_lock:
                    if not self.playing or self.reconnecting:
                        logger.info("收到退出信号，停止读取帧")
                        return

                for frame in packet.decode():
                    with self.state_lock:
                        if not self.playing or self.reconnecting:
                            return

                    try:
                        pil_frame = frame.to_image()
                        self.frame_count += 1
                        self.last_frame_received = time.time()

                        with self.queue_lock:
                            if self.frame_queue.full():
                                self.frame_queue.get_nowait()
                            self.frame_queue.put(pil_frame, block=False)

                    except Exception as e:
                        logger.warning(f"帧转换失败: {str(e)}")
                        continue

        except Exception as e:
            logger.error(f"帧读取循环异常: {str(e)}")
            raise

    def _start_reconnecting(self):
        with self.state_lock:
            self.reconnecting = True
            self.reconnect_attempts += 1

        logger.warning(f"视频流断开，开始重连（第{self.reconnect_attempts}次），间隔{self.reconnect_interval}秒")
        if self.status_callback:
            self.status_callback(f"reconnecting({self.reconnect_attempts})")

        start_time = time.time()
        while time.time() - start_time < self.reconnect_interval:
            with self.state_lock:
                if not self.playing:
                    logger.info("重连过程中收到停止指令，取消重连")
                    return
            time.sleep(0.1)

    def _clear_queue(self):
        with self.queue_lock:
            while not self.frame_queue.empty():
                try:
                    self.frame_queue.get_nowait()
                except queue.Empty:
                    break

    def _cleanup_resources(self):
        if self.container:
            try:
                self.container.close()
            except Exception as e:
                logger.error(f"关闭视频流失败: {str(e)}")
        self.container = None
        self.stream = None

    def _is_no_signal(self, frame):
        if not self.no_signal_reference:
            return False
        if frame.size != self.no_signal_reference.size:
            return False
        diff = ImageChops.difference(frame, self.no_signal_reference)
        bbox = diff.getbbox()
        if not bbox:
            return True
        width, height = frame.size
        total_area = width * height
        diff_area = (bbox[2] - bbox[0]) * (bbox[3] - bbox[1])
        return (diff_area / total_area) < 0.01


# 全局实例与C++调用接口（保持不变）
_media_instance = None
_instance_lock = threading.Lock()


def media_create(config_path):
    global _media_instance
    with _instance_lock:
        if _media_instance:
            _media_instance.stop()
        _media_instance = Media(config_path)
        logger.info(f"创建Media实例，配置路径: {config_path}")
        return True
    return False


def media_start():
    global _media_instance
    with _instance_lock:
        if _media_instance:
            return _media_instance.start()
        logger.error("未创建Media实例，无法启动")
        return False


def media_stop():
    global _media_instance
    with _instance_lock:
        if _media_instance:
            result = _media_instance.stop()
            return result
        logger.error("未创建Media实例，无需停止")
        return False


def media_get_frame():
    global _media_instance
    if _media_instance and _media_instance.is_running():
        return _media_instance.get_latest_frame()
    return None


def media_get_status():
    global _media_instance
    if _media_instance:
        return _media_instance.get_status()
    return "not_initialized"


def media_is_running():
    global _media_instance
    if _media_instance:
        return _media_instance.is_running()
    return False


# 测试入口
if __name__ == "__main__":
    media_create("./configs/player.yaml")
    media_start()

    try:
        while media_is_running():
            status = media_get_status()
            print(f"状态: {status}")
            time.sleep(1)
    except KeyboardInterrupt:
        print("收到中断信号，停止Media")
    finally:
        media_stop()